﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Diagnostics;

using LowLevelGraphics.Types.Interval;

namespace LowLevelGraphics.ColorSpaces
{
    /// <summary>
    /// HSB Channel
    /// </summary>
    public enum HSBChannel
    {
        /// <summary>
        /// 
        /// </summary>
        H = 0,
        /// <summary>
        /// 
        /// </summary>
        S = 1,
        /// <summary>
        /// 
        /// </summary>
        B = 2
    }

    /// <summary>
    /// HSB color space
    /// </summary>
    [DebuggerDisplay("H: {m_H}, S: {m_S}, B: {m_B}")]
    public class HSB : AbstractColorSpace
    {
        protected float m_H = 0.0f;
        protected float m_S = 0.0f;
        protected float m_B = 0.0f;

        protected FloatInterval m_fRangeH = new FloatInterval(0.0f, 360.0f);
        protected FloatInterval m_fRangeS = new FloatInterval(0.0f, 1.0f);
        protected FloatInterval m_fRangeB = new FloatInterval(0.0f, 1.0f);

        /// <summary>
        /// primary constructor
        /// </summary>
        /// <param name="_color"></param>
        public HSB(Color _color)
            : base(_color)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HSB"/> class.
        /// </summary>
        /// <param name="_H">The _ H.</param>
        /// <param name="_S">The _ S.</param>
        /// <param name="_B">The _ B.</param>
        public HSB(float _H, float _S, float _B)
        {
            m_H = _H;
            m_S = _S;
            m_B = _B;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HSB"/> class.
        /// </summary>
        internal HSB()
        {
        }

        /// <summary>
        /// this is used by clone to create a deep copy
        /// </summary>
        /// <param name="_hsb"></param>
        internal HSB(HSB _hsb)
        {
            this.m_H = _hsb.m_H;
            this.m_B = _hsb.m_B;
            this.m_S = _hsb.m_S;
        }

        /// <summary>
        /// Enable casting into operator
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static explicit operator HSB(Color _color)
        {
            return new HSB(_color);
        }

        /// <summary>
        /// gets or sets h
        /// </summary>
        public float H
        {
            get { return m_H; }
            set { m_H = value; }
        }

        /// <summary>
        /// gets or sets s
        /// </summary>
        public float S
        {
            get { return m_S; }
            set { m_S = value; }
        }

        /// <summary>
        /// gets or sets b
        /// </summary>
        public float B
        {
            get { return m_B; }
            set { m_B = value; }
        }

        /// <summary>
        /// Normalize
        /// </summary>
        /// <returns></returns>
        public override AbstractColorSpace Normalize()
        {
            return base.Normalize();
        }

        /// <summary>
        /// Normalize To RGB Color
        /// </summary>
        /// <returns></returns>
        public override Color NormalizeToRGBColor()
        {
            return base.NormalizeToRGBColor();
        }

        /// <summary>
        /// retransform this to rgb color space
        /// </summary>
        /// <returns></returns>
        public override Color ToColor()
        {
            return ColorFromAhsb(m_Color.A, m_H, m_S, m_B);
        }

        /// <summary>
        /// Calc Values
        /// </summary>
        protected override void CalcValues()
        {
            m_H = m_Color.GetHue();
            m_B = m_Color.GetBrightness();
            m_S = m_Color.GetSaturation();
        }

        /// <summary>
        /// Transform
        /// </summary>
        /// <param name="a"></param>
        /// <param name="h"></param>
        /// <param name="s"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private Color ColorFromAhsb(int a, float h, float s, float b)
        {
            if (0 > a) { a = 0; }
            if (255 < a) { a = 255; }

            if (0f > h) { h = 360f; }
            if (360f < h) { h = 360f; }

            if (0f > s) { s = 0; }
            if (1f < s) { s = 1; }

            if (0f > b) { b = 0.5f; }
            if (1f < b) { b = 1f; }

            if (0 == s)
            {
                return Color.FromArgb(a, Convert.ToInt32(b * 255),
                  Convert.ToInt32(b * 255), Convert.ToInt32(b * 255));
            }

            float fMax, fMid, fMin;
            int nSextant, iMax, iMid, iMin;

            if (0.5 < b)
            {
                fMax = b - (b * s) + s;
                fMin = b + (b * s) - s;
            }
            else
            {
                fMax = b + (b * s);
                fMin = b - (b * s);
            }

            nSextant = (int)Math.Floor(h / 60f);
            if (300f <= h)
            {
                h -= 360f;
            }
            h /= 60f;
            h -= 2f * (float)Math.Floor(((nSextant + 1f) % 6f) / 2f);
            if (0 == nSextant % 2)
            {
                fMid = h * (fMax - fMin) + fMin;
            }
            else
            {
                fMid = fMin - h * (fMax - fMin);
            }

            iMax = Convert.ToInt32(fMax * 255);
            iMid = Convert.ToInt32(fMid * 255);
            iMin = Convert.ToInt32(fMin * 255);

            switch (nSextant)
            {
                case 1:
                    return Color.FromArgb(a, iMid, iMax, iMin);
                case 2:
                    return Color.FromArgb(a, iMin, iMax, iMid);
                case 3:
                    return Color.FromArgb(a, iMin, iMid, iMax);
                case 4:
                    return Color.FromArgb(a, iMid, iMin, iMax);
                case 5:
                    return Color.FromArgb(a, iMax, iMin, iMid);
                default:
                    return Color.FromArgb(a, iMax, iMid, iMin);
            }
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("Color H = {0}, S = {1}, B = {2}", m_H, m_S, m_B);
        }

        /// <summary>
        /// Gets the values.
        /// </summary>
        /// <returns></returns>
        public override object[] GetValues()
        {
            return new object[] { m_H, m_S, m_B };
        }

        /// <summary>
        /// Sets the values.
        /// </summary>
        /// <param name="_aObject">The _a object.</param>
        public override void SetValues(object[] _aObject)
        {
            this.m_H = (float)_aObject[0];
            this.m_S = (float)_aObject[1];
            this.m_B = (float)_aObject[2];
        }

        /// <summary>
        /// Used to create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new HSB(this);
        }
    }
}
